package aes.a2;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;

import net.spy.memcached.AddrUtil;
import net.spy.memcached.MemcachedClient;

import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class Supplier {

	private static String[] BrandList = null;

	private static HashMap<String, Element> BrandModel = null;

	private static MemcachedClient mc;

	private Supplier() {
		loadData();

		try {
			//mc = new MemcachedClient( new InetSocketAddress("127.0.0.1",11211));
			mc=new MemcachedClient(
			        AddrUtil.getAddresses("127.0.0.1:11211"));
		} catch (Exception e) {
			// memcache initialization failed
			System.out.println("Mem cache init failed");
		}

	}

	private static Supplier instance = null;

	public static Supplier getInstance() {
		if (instance == null) {
			instance = new Supplier();
		}
		return instance;
	}

	private static Document document = null;

	private static void loadData() {
		File dir = new File("E:\\aes2data");
		FilenameFilter filter = new FilenameFilter() {
			public boolean accept(File dir, String name) {
				return name.endsWith(".xml");
			}
		};

		File[] files = dir.listFiles(filter);
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		DocumentBuilder db = null;
		try {
			db = dbf.newDocumentBuilder();
		} catch (ParserConfigurationException e1) {
			e1.printStackTrace();
		}

		document = db.newDocument();
		Element root = (Element) document.appendChild(document
				.createElement("Bikes"));

		for (File xmlFile : files) {
			Document doc;
			try {
				doc = db.parse(xmlFile);
				NodeList result = getNodeList(doc, "/Bikes/Manufacturer");

				BrandList = new String[result.getLength()];
				BrandModel = new HashMap<String, Element>();

				for (int i = 0; i < result.getLength(); i++) {
					Element manuNode = (Element) document.importNode(
							result.item(i), true);
					root.appendChild(manuNode);
					String manuName = XmlUtils.getXPath().evaluate("@name",
							manuNode);
					BrandModel.put(manuName, manuNode);
					BrandList[i] = manuName;
				}
			} catch (SAXException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			} catch (XPathExpressionException e) {
				e.printStackTrace();
			}
		}
	}

	public String getBrands() {
		if (mc != null) {
			Object brandXML = null;
			Future<Object> f = mc.asyncGet("brandXML");
			try {
				brandXML = f.get(5, TimeUnit.SECONDS);
				System.out.println("brands from cache");
				return brandXML.toString();
			} catch (Exception e) {
				// cancel the operation in case of timeout.
				f.cancel(false);
				
			}
		}
		Document doc = XmlUtils.createNewXMLDocument();
		Element root = (Element) doc.appendChild(doc.createElement("Bikes"));

		for (String manuName : BrandList) {
			Element manuNode = (Element) root.appendChild(doc
					.createElement("Manufacturer"));
			manuNode.setAttribute("name", manuName);
		}
		
		if(mc !=null){
			mc.set("brandXML", 86400, XmlUtils.printXML(doc));
			System.out.println("brands written to cache");
		}
		return XmlUtils.printXML(doc);
	}

	public String getModel(String brandName) {
		System.out.println("key: "+brandName);
		if (mc != null) {
			Object brandNameXML = null;
			Future<Object> f = mc.asyncGet(brandName.replaceAll("\\W",""));
			try {
				brandNameXML = f.get(5, TimeUnit.SECONDS);
				System.out.println(brandName+" from cache");
				return brandNameXML.toString();
			} catch (Exception e) {
				// cancel the operation in case of timeout.
				f.cancel(false);
				
			}
		}
		
		Element manuNode = BrandModel.get(brandName);
		NodeList modelList = manuNode.getElementsByTagName("Model");

		if (modelList == null) {
			return null;
		}

		Document doc = XmlUtils.createNewXMLDocument();
		Element root = (Element) doc.appendChild(doc.createElement("Bikes"));
		Element newManu = (Element) root.appendChild(doc
				.createElement("Manufacturer"));
		try {
			newManu.setAttribute("name",
					XmlUtils.getXPath().evaluate("@name", manuNode));
		} catch (DOMException e) {
			e.printStackTrace();
		} catch (XPathExpressionException e) {
			e.printStackTrace();
		}

		for (int i = 0; i < modelList.getLength(); i++) {
			Element modelNode = (Element) newManu.appendChild(doc
					.createElement("Model"));
			try {
				modelNode.setAttribute("name",
						XmlUtils.getXPath()
								.evaluate("@name", modelList.item(i)));
				modelNode.setAttribute(
						"price",
						XmlUtils.getXPath().evaluate("@price",
								modelList.item(i)));
				modelNode.setAttribute("year",
						XmlUtils.getXPath()
								.evaluate("@year", modelList.item(i)));
			} catch (DOMException e) {
				e.printStackTrace();
			} catch (XPathExpressionException e) {
				e.printStackTrace();
			}
		}
		
		if(mc !=null){
			mc.set(brandName.replaceAll("\\W",""), 86400, XmlUtils.printXML(doc));
			System.out.println(brandName+" details written");
		}
		return XmlUtils.printXML(doc);

	}

	private static NodeList getNodeList(Node document, String xpathString) {
		try {
			return (NodeList) XmlUtils.getXPath().evaluate(xpathString,
					document, XPathConstants.NODESET);
		} catch (XPathExpressionException e) {
			e.printStackTrace();
			return null;
		}
	}

	public String getDetails(String brandName, String modelName) {
		if (mc != null) {
			Object brandDetails = null;
			Future<Object> f = mc.asyncGet(brandName.replaceAll("\\W","")+modelName.replaceAll("\\W",""));
			try {
				brandDetails = f.get(5, TimeUnit.SECONDS);
				System.out.println(brandName+":"+modelName+" from cache");
				return brandDetails.toString();
			} catch (Exception e) {
				// cancel the operation in case of timeout.
				f.cancel(false);
				
			}
		}
		Element model = null;
		try {
			model = (Element) XmlUtils.getXPath().evaluate(
					"/Bikes/Manufacturer[@name=\"" + brandName
							+ "\"]/Model[@name=\"" + modelName + "\"]",
					document, XPathConstants.NODE);
		} catch (XPathExpressionException e) {
			e.printStackTrace();
		}

		if (model != null) {
			if(mc !=null){
				mc.set(brandName.replaceAll("\\W","")+modelName.replaceAll("\\W",""), 86400, XmlUtils.printXML(model));
				System.out.println(brandName+":"+modelName+" details written");
			}
			return XmlUtils.printXML(model);
		} else {
			return null;
		}
	}

}